\chapter{Retrieval}\label{cap:retrieval}
Una parte fondamentale del progetto è proprio quella riguardante il \var{Retrieval}, che gestisce tutte le operazioni riguardanti l'indice dei documenti e la ricerca. In particolare si è realizzato un sistema che sfrutta la libreria \var{Lucene} per gestire in modo semplice l'indice.
\section{Documenti}\label{cap:retrieval:docs}
Il software che si è realizzato si basa fortemente sulla gestione dei documenti registrati nell'indice, quindi risulta fondamentale riportare la struttura dei documenti.
Ogni documento ha un riferimento uno ad uno con una pagina web recuperata, infatti vengono salvati ed indicizzati i seguenti campi:
\begin{description}
\item[URL:] tale campo contiene semplicemente l'\var{URL} da cui è stato possibile raggiungere la pagina in questione;
\item[Title:] contiene il titolo della pagina;
\item[Description:] alcune pagine web hanno dei tag speciali che contengono la descrizione del contenuto della pagina;
\item[Keywords:] le parole chiavi contenute nel tag keywords;
\item[Keyphrases:] simili alle keywords, ma più rare;
\item[Body:] contiene il contenuto del tag body (filtrato), ovvero gran parte della pagina recuperata;
\item[Date:] sarebbe la data di scadenza della pagina, viene utilizzata per aggiornare un documento nell'indice in caso se ne reperisca uno più recente;
\end{description}
Tutti i campi elencati sono necessari per poter effettuare una ricerca sufficientemente rilevante per la portata del progetto. Come può essere facilmente intuito, il campo più importante risulta essere \var{Body}, oltre all'indispensabile \var{URL} che viene usato come chiave per il documento; infatti non possono coesistere nell'indice più documenti con lo stesso \var{URL}.
Ogni documento che viene inserito nell'indice deve passare una serie di controlli di integrità. Nel caso del body vengono effettuate delle operazioni di pulitura del testo; in particolare vengono rimosse tutte le parole che riguardano codice o quant'altro. Grazie a quest'ultima operazione si ottiene un indice molto più pulito e rilevante per le ricerche.
\section{Indicizzatore}\label{cap:retrieval:indexer}
Un'altra parte importante del software in questione è l'\var{Indicizzatore} (o \var{Indexer}) che si occupa della gestione vera e propria dell'indice dei documenti. In particolare, l'Indexer consente di aggiungere dei documenti nell'indice, evitando l'inserimento di duplicati (vedere \ref{cap:retrieval:docs}); oltretutto è anche in grado di sfruttare il campo \var{Date} per un eventuale aggiornamento del documento. L'Indexer viene chiamato ad intervalli di tempo regolari per consentirgli di indicizzare tutti i documenti rilevanti trovati.
Questo modulo è risultato essere uan parte del progetto in cui viene speso gran parte del tempo, a causa dell'elevato numero di nuovi documenti da aggiungere (o modificare) nell'indice.
\section{Ricerca}\label{cap:retrieval:searcher}
La ricerca è un altro tassello fondamentale per il raggiungimento degli obiettivi del progetto; infatti la ricerca è necessaria sia per filtrare i documenti in base al task e sia effettuare delle ricerche sui documenti filtrati. Per tali motivi sono stati realizzati due moduli, uno per ogni tipo di ricerca.
\subsection{Ricerca task}
Il primo tipo di ricerca genera una query a partire dal task richiesto, in particolare vengono espletati i seguenti passi:
\begin{enumerate}
\item si prende il task inserito dall'utente, che potrebbe essere ``Università Tor Vergata'', e lo si divide in token (Università, Tor, Vergata);
\item per ogni campo (``keywords'', ``title'', ``body'') si prepara una query del seguente tipo:
\begin{center}
\texttt{title:Università AND title:Tor AND title:Vergata}
\end{center}
\item in seguito si mettono in OR tutte le query preparate per ottenere:
\begin{center}
\texttt{(title:Università AND title:Tor AND title:Vergata) OR (...) OR (...)}
\end{center}
\end{enumerate}
Formulando tale query non si fa altro che ricercare nell'indice uno o più documenti con i parametri richiesti. Si deciso di imporre il vincolo che l'intero task deve essere presente in almeno un campo, questo per rendere più rilevanti i risultati della ricerca. In principio si erano impostati altri tipi di vincoli, in particolare era sufficiente che un solo token fosse presente in un qualsiasi campo per rendere il documento rilevante; il che rendeva un set di risultati molto scadenti.
\subsection{Ricerca query (Wordnet - Rocchio)}
Per quando riguarda la ricerca all'interno dei documenti già filtrati tramite ``ricerca task'', si è deciso di implementare un sistema molto più complesso ed efficace. Il testo di ricerca che viene inserito dall'utente può risultare poco specifico, il che rende i risultati molto vaghi; per evitare di incorrere in tali problemi si è deciso di implementare un sistema di \var{Query Expansion}. In principio si è realizzato un modulo di espansione basato su \var{Wordnet}, in seguito se ne è aggiunto uno basato su \var{Rocchio} che ha avuto un impatto migliore sui risultati.
\begin{figure}[H]
\begin{lstlisting}[frame=trBL]
isearcher = new IndexSearcher(indexDir);
queryString = queryString.replaceAll("%20", " ");

StandardAnalyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser("body", analyzer);
Query query = parser.parse(queryString);

Debug.println("Normal: "+query.toString(), 1);
result = isearcher.search(query);
Debug.println("Search hits: "+result.length(), 1);

Query expandedQuery = this.expandQuery(query, query.toString(), result, isearcher, analyzer, type);
Debug.println("Expanded: "+expandedQuery.toString(), 1);
result = isearcher.search(expandedQuery);
Debug.println("Search with expanded query hits: "+result.length(), 1);
\end{lstlisting}
\end{figure}
Il funzionamento della ricerca nell indice è riportato di seguito:
\begin{enumerate}
\item viene inizializzato il modulo di ricerca nell'indice;
\item eventualmente si pulisce la stringa di query che potrebbe contenere caratteri speciali come \code{\%20}, dovuti alla pagina jsp;
\item si avvia la ricerca con la query ricevuta;
\item in caso di Rocchio i risultati ottenuti vengono utilizzati per l'espansione della query. In caso di Wordnet, invece, si espande chiamando il modulo adeguato;
\item si effettua la ricerca con la nuova query espansa;
\end{enumerate}
\paragraph{Wordnet}
Tale componente è formato da:
\begin{enumerate}
\item un file, scritto in \var{Prolog}, contenente tutte le parole di \var{Wordnet};
\item un modulo in grado di indicizzare le parole in un indice tramite \var{Lucene};
\item un altro modulo che ricerca tutti i sinonimi delle parola che si richiede.
\end{enumerate}
Utilizzando questo sistema si è raggiunta una precisione nei risultati più elevata. In alcuni casi si incombe in ambiguità a causa del constesto in cui si trovano le parole, ad esempio se si espande una query del tipo ``Apple'' il sistema aggiunge parole del tipo ``fruit'', il che non sarebbe sbagliato se noi stessimo cercando il frutto; ma se si volesse ricercare la società ``Apple'' allora il risultato di Wordnet sarebbe totalmente errato.
\paragraph{Rocchio}
Per evitare di incorrere in problemi come quelli appena citati si è realizzato un altro modulo di espansione query utilizzando il sistema di \var{Pseudo-relevance feedback Rocchio}. Tale metodo funziona nel seguente modo:
\begin{enumerate}
\item si prende la query desiderata dall'utente e si effettua la ricerca nell'indice;
\item con i risultati ottenuti si effettua lo pseudo-relevance feedback considerando rilevanti i primi N documenti ed irrilevanti gli ultimi M;
\item si prendono i termini dei documenti rilevanti e si aggiungono alla query originale;
\item si sottraggono i termini dei documenti non rilevanti;
\item viene rieseguita una nuova ricerca con la nuova query.
\end{enumerate}
La formula utilizzata per l'espansione è la seguente:
$$
\overrightarrow{q}_{m} = \alpha\overrightarrow{q} + \dfrac{\beta}{\vert D_{r}\vert}\sum_{\forall\overrightarrow{d_{j}}\in D_{r}}\overrightarrow{d_{j}} - \dfrac{\gamma}{\vert D_{n}\vert}\sum_{\forall\overrightarrow{d_{j}}\in D_{n}}\overrightarrow{d_{j}}
$$
E' stata implementata una variante di \var{Rocchio} aggiungendo ai nuovi termini della query il loro peso \var{tf-idf}; in particolare la formula è diventata:
$$
\overrightarrow{q}_{m} = \alpha\overrightarrow{q} + \dfrac{\beta}{\vert D_{r}\vert}\sum_{\forall\overrightarrow{d_{j}}\in D_{r}}\overrightarrow{w_{j}}\overrightarrow{d_{j}} - \dfrac{\gamma}{\vert D_{n}\vert}\sum_{\forall\overrightarrow{d_{j}}\in D_{n}}\overrightarrow{w_{j}}\overrightarrow{d_{j}}
$$
in cui il simbolo $\overrightarrow{w_{j}}$ indica il vettore dei pesi \var{tf-idf} dei termini nel documento $\overrightarrow{d_{j}}$. Tale funzionalità è risultata poco rilevante ai fini della precisione considerando che \var{Lucene} utilizza già tale pesatura nella ricerca dell'indice, quindi questa variante può essere disabilitata dall'utente in base alle sue preferenze.
Il metodo \var{Rocchio} (senza variante) ha consentito un aumento della precisione nei documenti ritrovati, infatti a differenza del metodo \var{Wordnet} è molto più difficile incorrere in ambiguità delle parole. Un punto a favore di \var{Wordnet} può andare sotto l'aspetto della espressività della query, questo perchè con il sistema \var{Rocchio} realizzato non possono essere fatte espansioni di query che specificano il campo (``URL'', ``title'' ecc.) del termine. Per evitare un'elevata complessità computazionale è stata impostata una espansione che considera soltanto il campo ``body''. Tale vincolo ha senso anche sotto altri aspetti, come il fatto che gran parte del contenuto informativo di una pagina web è proprio nel ``body''.